﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Novacode;
using System.Drawing;
using Microsoft.TeamFoundation.TestManagement.Client;
using System.Windows.Forms;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.IO;
using System.Reflection;

namespace TestScribeExtended
{
    class TestScribeGenerator
    {
        private DocX _document;
        private ITestManagementTeamProject _tmsProject;
        private Color azulPadrao = Color.FromArgb(79, 129, 189);
        private string _destinationFolder;
        private string _destinationFile;

        private Assembly _assembly;

        public TestScribeGenerator()
        {
            _assembly = Assembly.GetAssembly(this.GetType());
        }

        private void AddTestPlanParagraph(string text)
        {
            var testPlanPicture = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.testplan.jpg")).CreatePicture();
            var p = _document.InsertParagraph();
            testPlanPicture.Height = 16;
            testPlanPicture.Width = 16;
            p.AppendPicture(testPlanPicture);
            AddSection(text, p);
        }

        private static void AddSection(string text, Paragraph p)
        {

            p.Append(text)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(9)
            .Color(Color.FromArgb(79, 129, 189))
            .Bold();

        }

        private void AddTestPlanDetailsParagraph(string description, string status, string activeDates, string area, string iteration, string build, string buildDefinition, string buildQuality)
        {
            var p = _document.InsertParagraph();

            p.Append(description)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(7)
            .AppendLine();

            AddTestPlanDetailsField("Status: ", status, p);
            AddTestPlanDetailsField(" | Active dates: ", activeDates, p);
            AddTestPlanDetailsField(" | Area: ", area, p);
            AddTestPlanDetailsField(" | Iteration: ", iteration, p);
            AddTestPlanDetailsField(" | Build: ", build, p);
            AddTestPlanDetailsField(" | Build definition: ", buildDefinition, p);
            AddTestPlanDetailsField(" | Build quality: ", buildQuality, p);
        }

        private static void AddTestPlanDetailsField(string fieldName, string fieldValue, Paragraph p)
        {
            if (fieldValue == null) fieldValue = "<Not assigned>";
            p.Append(fieldName)
            .Color(Color.FromArgb(128, 128, 128))
            .Font(new FontFamily("Segoe UI"))
            .FontSize(7)
            .Append(fieldValue)
            .Color(Color.Black)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(7);
        }

        private void AddTestPlanHeader()
        {
            Novacode.Picture testPlanPicture = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.testplan.jpg")).CreatePicture();
            testPlanPicture.Width = 32;
            testPlanPicture.Height = 32;

            var p = AddTestPlanTitle("Test Plan Details", testPlanPicture);
            AddTestPlanTitleDescription(p, "A test plan lets you specify what you want to test and how to run those tests. A test plan can be applied to a specific iteration of your project. You can have just one default test suite for your test cases, or you can create a test suite hierarchy.");
            p.AppendLine();
            AddTestPlanTitleDescription(p, "You can also select the default configurations to use to run the tests in your test plan. The test configuration informs the tester of the set up that is required for these tests. You can have one or multiple default configurations. A test result is recorded every time that you run the test with a specific configuration. A test plan enables you to measure your testing progress, based on these test results for the test and configuration pairings.");
            
        }

        private static void AddTestPlanTitleDescription(Paragraph p, string description)
        {
            p.AppendLine();
            p.Append(description)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(8)
            .Color(Color.FromArgb(89, 89, 89))
            .Italic();
        }

        private Paragraph AddTestPlanTitle(string title, Novacode.Picture pic)
        {
            var p = _document.InsertParagraph();
            if (pic != null)
            {
                p.AppendPicture(pic);
            }
            p.Append(title)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(14)
            .Color(Color.FromArgb(54, 95, 145))
            .Bold();
            return p;
        }

        private void AddSuiteHierarchy(TreeNode testPlanNode)
        {
            var p = _document.InsertParagraph();
            AddSection("Suite Hierarchy", p);

            Novacode.Picture staticSuitePic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.staticsuite.jpg")).CreatePicture();
            Novacode.Picture reqSuitePic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.reqsuite.jpg")).CreatePicture();
            Novacode.Picture dynamicSuitePic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.dynamicsuite.jpg")).CreatePicture();
            staticSuitePic.Height = reqSuitePic.Height = dynamicSuitePic.Height = 16;
            staticSuitePic.Width = reqSuitePic.Width = dynamicSuitePic.Width = 16;

            p = _document.InsertParagraph();
            AddSuiteHierarchyItem(testPlanNode, p, staticSuitePic, reqSuitePic, dynamicSuitePic, 0);
            
        }
        private void AddSuiteHierarchyItem(TreeNode testNode, Paragraph p, Novacode.Picture staticSuitePic, Novacode.Picture reqSuitePic, Novacode.Picture dynamicSuitePic, int level)
        {
            
            p.Append(new string(' ', 5 * level));
            if (testNode.Tag is ITestPlan || testNode.Tag is IStaticTestSuite)
            {
                p.AppendPicture(staticSuitePic);
            }
            else if (testNode.Tag is IRequirementTestSuite)
            {
                p.AppendPicture(reqSuitePic);
            }
            else
            {
                p.AppendPicture(dynamicSuitePic);
            }
            p.Append(testNode.Text)
            .Font(new FontFamily("Segoe UI"))
            .FontSize(7)
            .AppendLine();

            level++;
            foreach (TreeNode node in testNode.Nodes)
            {
                AddSuiteHierarchyItem(node, p, staticSuitePic, reqSuitePic, dynamicSuitePic, level);
            }
        }

        private void AddConfigurations()
        {
            var p = _document.InsertParagraph();
            Novacode.Picture configurationPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.configuration.jpg")).CreatePicture();
            var configurations = _tmsProject.TestConfigurations.Query("SELECT * FROM TestConfiguration");

            AddSection(string.Format("Available Configurations ({0})", configurations.Count), p);

            foreach (var configuration in configurations)
            {
                p.AppendLine()
                .AppendPicture(configurationPic)
                .Append(configuration.Name)
                .Font(new FontFamily("Segoe UI"))
                .Color(Color.FromArgb(79, 129, 189))
                .FontSize(8);
            }

        }

        private void AddTestSettings()
        {
            var p = _document.InsertParagraph();
            Novacode.Picture configurationPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.testsettings.jpg")).CreatePicture();
            AddSection("Test Settings", p);
            var testSettings = _tmsProject.TestSettings.Query("SELECT * FROM TestSettings");
            foreach (var testSetting in testSettings)
            {
                p.AppendLine()
                .AppendPicture(configurationPic)
                .Append(testSetting.Name)
                .Font(new FontFamily("Segoe UI"))
                .Color(Color.FromArgb(79, 129, 189))
                .FontSize(8);
            }
        }

        public void Generate(List<TreeNode> testPlanNodes, ITestManagementTeamProject tmsProject, string destinationFolder)
        {
            this._tmsProject = tmsProject;
            this._destinationFolder = destinationFolder;
            
            foreach (var testPlanNode in testPlanNodes)
            {
                using (_document = DocX.Load(_assembly.GetManifestResourceStream("TestScribeExtended.Templates.template.docx")))
                {
                    var testPlan = (ITestPlan)testPlanNode.Tag;
                    string textPlanText = string.Format("Test plan {0}: {1}", testPlan.Id, testPlan.Name);
                    _destinationFile = string.Format("test plan - {0} - {1}.docx", testPlan.Name, DateTime.Now.ToString("yyyyMMddHHmmss"));

                    AddTestPlanHeader();
                    AddTestPlanParagraph(textPlanText);
                    AddTestPlanDetailsParagraph(testPlan.Description, testPlan.State.ToString(), testPlan.StartDate.ToShortDateString() + " - " + testPlan.EndDate.ToShortDateString(), testPlan.AreaPath, testPlan.Iteration, testPlan.BuildNumber, testPlan.BuildFilter.BuildDefinition, testPlan.BuildFilter.BuildQuality);
                    AddConfigurations();
                    AddTestSettings();
                    AddSuiteHierarchy(testPlanNode);
                    AddSuiteDetails();
                    AddSuiteState(testPlan.Id, testPlan.Name, testPlan.State.ToString());
                    AddSuiteConfigurations();
                    AddSuiteTestCases(testPlan.RootSuite.AllTestCases);
                    _document.SaveAs(Path.Combine(_destinationFolder, _destinationFile));
                }
            }

        }

        private void AddSuiteTestCases(ITestCaseCollection testCaseCollection)
        {
            var p = _document.InsertParagraph();
            var section = string.Format("Test Cases ({0})", testCaseCollection.Count);
            AddSection(section, p);
            Novacode.Picture testCasePic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.testcase.jpg")).CreatePicture();
            testCasePic.Height = testCasePic.Width = 16;

            foreach (var testCase in testCaseCollection)
            {
                var title = string.Format("Test Case {0}: {1}", testCase.Id, testCase.Title);
                p = _document.InsertParagraph();
                p.AppendPicture(testCasePic)
                .Append(title)
                .Font(new FontFamily("Cambria"))
                .Bold()
                .Italic()
                .FontSize(9)
                .Color(Color.FromArgb(79, 129, 189));

                p.AppendLine();

                AddTestPlanDetailsField("Owner: ", testCase.Owner.DisplayName, p);
                AddTestPlanDetailsField(" | State: ", testCase.State, p);
                AddTestPlanDetailsField(" | Type: ", testCase.IsAutomated ? "Automated" : "Manual", p);
                AddTestPlanDetailsField(" | Area: ", testCase.WorkItem.AreaPath, p);
                AddTestPlanDetailsField(" | Iteration: ", testCase.WorkItem.IterationPath, p);
                AddTestPlanDetailsField(" | Automated test: ", testCase.WorkItem.Fields["Microsoft.VSTS.TCM.AutomationStatus"].Value.ToString(), p);
                AddTestPlanDetailsField(" | Assigned to: ", testCase.WorkItem.Fields[CoreField.AssignedTo].Value.ToString(), p);

                AddTestSteps(testCase.Actions);
                AddTestAttachments(testCase.Id);
                
            }
        }

        private void AddTestAttachments(int testCaseId)
        {

            var lastTestCaseResult = _tmsProject.TestResults.ByTestId(testCaseId).LastOrDefault();
            
            if (lastTestCaseResult != null)
            {
                var lastIteration = lastTestCaseResult.Iterations.LastOrDefault();
                if (lastIteration != null)
                {
                    string[] supportedExtensions = { ".WMV", ".XESC", ".PNG" };

                    var attachments = from att in lastIteration.Attachments
                                      where supportedExtensions.Contains(Path.GetExtension(att.Name).ToUpper())
                                      select att;
                        
                    if (attachments.Count() > 0)
                    {
                        var p = _document.InsertParagraph();
                        p.IndentationBefore = 0.5f;
                        var text = string.Format("Videos ({0})", attachments.Count());
                        p.Append(text)
                        .FontSize(8)
                        .Font(new FontFamily("Segoe UI"))
                        .Color(azulPadrao);

                        var relativeFolder = @"Attachments\" + Path.GetFileNameWithoutExtension(_destinationFile);
                        var folder = Path.Combine(_destinationFolder, relativeFolder);
                        if (!Directory.Exists(folder))
                        {
                            Directory.CreateDirectory(folder);
                        }

                        foreach (var attachment in attachments)
                        {
                            var tempFileName = Path.Combine(folder, attachment.Name);
                            int i = 0;
                            while (File.Exists(tempFileName))
                            {
                                tempFileName = Path.Combine(folder, Path.GetFileNameWithoutExtension(attachment.Name) + string.Format("({0}){1}", ++i, Path.GetExtension(attachment.Name)));
                            }
                            attachment.DownloadToFile(tempFileName);
                            Novacode.Hyperlink link = _document.AddHyperlink(Path.GetFileName(tempFileName), new Uri(relativeFolder + "\\" + Path.GetFileName(tempFileName), UriKind.Relative));
                            p = _document.InsertParagraph();
                            p.AppendHyperlink(link)
                            .FontSize(8)
                            .Font(new FontFamily("Segoe UI"))
                            .Color(azulPadrao)
                            .Italic();
                        }
                    }
                }
            }
            
        }

        private void AddTestSteps(TestActionCollection testSteps)
        {
            if (testSteps.Count == 0)
            {
                return;
            }

            Novacode.Picture testStepPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.teststep.jpg")).CreatePicture();
            Novacode.Picture testStepExpectedPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.teststepexpected.jpg")).CreatePicture();
            Novacode.Picture sharedStepPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.sharedstep.jpg")).CreatePicture();
            testStepPic.Width = testStepExpectedPic.Width = 
                testStepPic.Height = testStepExpectedPic.Height = 
                sharedStepPic.Width = sharedStepPic.Height = 16;
            
            var p = _document.InsertParagraph();
            p.IndentationBefore = 0.5f;
            var text = string.Format("Test Steps ({0})", testSteps.Count);
            p.Append(text)
            .FontSize(8)
            .Font(new FontFamily("Segoe UI"))            
            .Color(azulPadrao);

            var table = _document.InsertTable(testSteps.Count + 1, 4);
            table.Design = TableDesign.MediumList1Accent1;

            Formatting f = new Formatting();
            f.FontFamily = new FontFamily("Segoe UI");
            f.Size = 7;
            f.FontColor = Color.FromArgb(54, 95, 145);

            table.AutoFit = AutoFit.Contents;

            table.Rows[0].Cells[1].Paragraphs[0].InsertText("#", false, f);
            table.Rows[0].Cells[2].Paragraphs[0].InsertText("Title", false, f);
            table.Rows[0].Cells[3].Paragraphs[0].InsertText("Expected Value", false, f);

            int row = 0;
            int id = 0;
            foreach (var testStep in testSteps)
            {
                row++;
                id++;
                if (testStep is ITestStep)
                {
                    var step = (ITestStep)testStep;
                    if (string.IsNullOrWhiteSpace(step.ExpectedResult))
                    {
                        table.Rows[row].Cells[0].Paragraphs[0].InsertPicture(testStepPic);
                    }
                    else
                    {
                        table.Rows[row].Cells[0].Paragraphs[0].InsertPicture(testStepExpectedPic);
                    }

                    table.Rows[row].Cells[1].Paragraphs[0].InsertText(id.ToString(), false, f);
                    table.Rows[row].Cells[2].Paragraphs[0].InsertText(step.Title, false, f);
                    table.Rows[row].Cells[3].Paragraphs[0].InsertText(step.ExpectedResult, false, f);

                }
                else if (testStep is ISharedStepReference)
                {
                    var step = ((ISharedStepReference)testStep).FindSharedStep();

                    table.Rows[row].Cells[0].Paragraphs[0].InsertPicture(sharedStepPic);
                    table.Rows[row].Cells[1].Paragraphs[0].InsertText(id.ToString(), false, f);
                    table.Rows[row].Cells[2].Paragraphs[0].InsertText(step.Title, false, f);

                    var childId = 0;
                    foreach (var childStep in step.Actions)
                    {
                        table.InsertRow();
                        row++;
                        childId++;
                        var ts = (ITestStep)childStep;

                        if (string.IsNullOrWhiteSpace(ts.ExpectedResult))
                        {
                            table.Rows[row].Cells[0].Paragraphs[0].InsertPicture(testStepPic);
                        }
                        else
                        {
                            table.Rows[row].Cells[0].Paragraphs[0].InsertPicture(testStepExpectedPic);
                        }

                        table.Rows[row].Cells[1].Paragraphs[0].InsertText(string.Format("{0}.{1}", id, childId), false, f);
                        table.Rows[row].Cells[2].Paragraphs[0].InsertText(ts.Title, false, f);
                        table.Rows[row].Cells[3].Paragraphs[0].InsertText(ts.ExpectedResult, false, f);
                    }
                }
                
            }
            _document.InsertParagraph();

        }
                
        private void AddSuiteConfigurations()
        {
            var p = _document.InsertParagraph();
            Novacode.Picture configurationPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.configuration.jpg")).CreatePicture();
            configurationPic.Width = configurationPic.Height = 16;
            var configurations = _tmsProject.TestConfigurations.Query("SELECT * FROM TestConfiguration");

            AddSection(string.Format("Available Configurations ({0})", configurations.Count), p);

            foreach (var configuration in configurations)
            {
                p.AppendLine()
                .AppendPicture(configurationPic)
                .Append(configuration.Name)
                .Font(new FontFamily("Segoe UI"))
                .Color(azulPadrao)
                .FontSize(8);
            }
        }

        private void AddSuiteState(int id, string name, string state)
        {
            var p = _document.InsertParagraph();
            Novacode.Picture testPlanPic = _document.AddImage(_assembly.GetManifestResourceStream("TestScribeExtended.Images.staticsuite.jpg")).CreatePicture();
            testPlanPic.Width = 16;
            testPlanPic.Height = 16;
            string section = string.Format("Suite {0}: {1}", id, name);
            p.AppendPicture(testPlanPic);
            AddSection(section, p);
            p.AppendLine();
            AddTestPlanDetailsField("State: ", state, p);  
        }

        private void AddSuiteDetails()
        {
            var p = AddTestPlanTitle("Suite Details", null);
            AddTestPlanTitleDescription(p, "You can group your test cases together by organizing test cases into a test suite hierarchy in your test plan. By grouping your test cases together, when you want to run all these tests, you can select this test suite.");
        }
    }
}
